package main

import "fmt"

/*
*
异常处理：
Go没有机构化的异常。使用 panic 抛出错误，使用 recover 捕获异常
（结构化异常指的是c/c++ 程序语言中，程序化控制结构 try-except 和 try-finally 语句用于处理异常事件）
异常的使用场景简单描述为：Go 中可以抛出一个panic 的异常，然后在 defer 中通过 recover 捕获这个异常然后正常处理。

panic 的介绍
1.内置函数
2.假如函数F中书写了 panic 语句，会终止其后要执行的代码，在panic 所在函数F 中如果存在要执行的defer 函数列表，
按照defer 逆序执行
3.返回函数F 的调用者G 在 G中，调用函数F 语句之后的代码不会自信，加入函数G 中存在要执行的defer 函数列表，
按照defer 逆序执行
4. 直到整个退出，并报告错误

recover 介绍
1.内置函数
2.来控制一个goroutine 的 panicking 行为，捕获panic ,从而影响应用的行为
3.一般的调用建议

	a.在defer 函数中，通过recover来终止一个goroutine 的panicking 过程，从而恢复正常代码的执行
	b.可以通过获取panic传递的error

注意：
1. 利用recover 函数处理panic指令，defer 必须放在panic 之前定义，另外recover 只有在defer调用的函数中才有效。
否则当 panic 时，recover 无法捕获到panic 无法防止 panic 扩散。
2, recover 处理一异常之后，逻辑并不会恢复到panic 那个点去，函数跑到defer 之后的那个点。
3.多个defer 会形成 defer 栈，后定义的defer 语句会被最先调用。
*/
func main() {
	test()

}

// panic 和 recover 函数的配合使用
func test() {
	defer func() {
		if err := recover(); err != nil {
			println(err.(string)) // 将 interface{} 转型为具体类型。
		}
	}()

	panic("panic error!")
}

// 向已关闭的通道发送数据会引发 panic
func test2() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	var ch chan int = make(chan int, 10)
	close(ch)
	ch <- 1
}

// 延迟调用中引发的错误，可被后续延迟调用捕获，但仅最后一个错误可被捕获
func test3() {
	defer func() {
		fmt.Println(recover())
	}()

	defer func() {
		panic("defer panic")
	}()

	panic("test panic")
}

// 捕获函数recover 只有在 defer 延迟调用内，直接调用才会终止错误，否则总是返回 nil.任何未捕获的错误都会沿调用栈向上传递
func test4() {
	defer func() {
		fmt.Println(recover()) //有效
	}()
	defer recover()              //无效！
	defer fmt.Println(recover()) //无效！
	defer func() {
		func() {
			println("defer inner")
			recover() //没有在defer函数内直接调用，无效！
		}()
	}()

	panic("test panic")
}

//使用延迟匿名函数或下面这样都是有效的

//package main
//
//import (
//"fmt"
//)
//
//func except() {
//	fmt.Println(recover())
//}
//
//func test() {
//	defer except()
//	panic("test panic")
//}
//
//func main() {
//	test()
//}

//如果需要保护代码段，可将代码块重构成匿名函数，如此可确保后续代码可以被执。

//package main
//
//import "fmt"
//
//func test(x, y int) {
//	var z int
//
//	func() {
//		defer func() {
//			if recover() != nil {
//				z = 0
//			}
//		}()
//		panic("test panic")
//		z = x / y
//		return
//	}()
//
//	fmt.Printf("x / y = %d\n", z)  //panic + recover结束了匿名函数内部的执行，跳出了匿名函数。但这行代码仍然可以被执行。
//}
//
//func main() {
//	test(2, 1)
//}
